/*
 * matrixkeyboard.cpp
 *
 *  Created on: Dec 8, 2021
 *      Author: t1019
 */

#include "matrixkeyboard.h"

namespace bt_matrixkeyboard {

MatrixKeyboard::MatrixKeyboard(const uint8_t out_num, const uint8_t in_num)
{
	outputlines_num = out_num;
	inputlines_num = in_num;
	matrix = (s_MatrixKeyStatus*)new s_MatrixKeyStatus[out_num*in_num];
	for(int i = 0;i < out_num*in_num;i++)
	{
		matrix[i].timeout = 0;
		matrix[i].status = MKEY_NORMAL;
	}

	outputlines = NULL;
	inputlines = NULL;
	martixvalue = NULL;
	allow_multitriggering = false;
	queue = new char[queue_num+1];
	for(int i = 0;i < queue_num+1; i++)
		queue[i] = 0;
	scan_idx = outputlines_num;
}

void MatrixKeyboard::handler(uint16_t period)
{
	//读取检测线状态
	for(int j = 0;j < inputlines_num; j++)
		_keystatus_refresh(scan_idx,j,_port_gpioget(inputlines[j]),period);
	//更新控制线序号
	scan_idx++;
	if(scan_idx >= outputlines_num)
		scan_idx = 0;
	//更新控制线输出
	for(int i = 0;i < outputlines_num; i++)
		_port_gpioset(outputlines[i],true);
	_port_gpioset(outputlines[scan_idx],false);
}

void MatrixKeyboard::_keystatus_refresh(uint8_t o, uint8_t i, bool val, uint16_t period)
{
	switch(matrix[o+i*outputlines_num].status)
	{
	case MKEY_DET_PRESSED:
		if(!val)
		{
			matrix[o+i*outputlines_num].timeout += period*outputlines_num;
			if(matrix[o+i*outputlines_num].timeout >= change_timeout)
			{
				//按下瞬间生成消息
				_append_event(martixvalue?martixvalue[o+i*outputlines_num]:'0');
				if(allow_multitriggering)
					matrix[o+i*outputlines_num].status = MKEY_PRESSING_CD;
				else
					matrix[o+i*outputlines_num].status = MKEY_PRESSING;
				matrix[o+i*outputlines_num].timeout = 0;
			}
		}
		else
		{
			matrix[o+i*outputlines_num].status = MKEY_NORMAL;
			matrix[o+i*outputlines_num].timeout = 0;
		}
		break;
	case MKEY_PRESSING:
		if(val)
		{
			matrix[o+i*outputlines_num].status = MKEY_DET_RELEASED;
			matrix[o+i*outputlines_num].timeout = 0;
		}
		break;
	case MKEY_PRESSING_CD:
		if(!val)
		{
			matrix[o+i*outputlines_num].timeout += period*outputlines_num;
			if(matrix[o+i*outputlines_num].timeout >= trig_timeout)
			{
				matrix[o+i*outputlines_num].timeout = 0;
				//长按重复生成消息
				_append_event(martixvalue?martixvalue[o+i*outputlines_num]:'0');
			}
		}
		else
		{
			matrix[o+i*outputlines_num].status = MKEY_DET_RELEASED;
			matrix[o+i*outputlines_num].timeout = 0;
		}
		break;
	case MKEY_DET_RELEASED:
		if(!val)
		{
			if(allow_multitriggering)
				matrix[o+i*outputlines_num].status = MKEY_PRESSING_CD;
			else
				matrix[o+i*outputlines_num].status = MKEY_PRESSING;
			matrix[o+i*outputlines_num].timeout = 0;
		}
		else
		{
			matrix[o+i*outputlines_num].timeout += period*outputlines_num;
			if(matrix[o+i*outputlines_num].timeout >= change_timeout)
			{
				//抬起生成消息
				//_append_event(martixvalue?martixvalue[o+i*outputlines_num]:'0');
				matrix[o+i*outputlines_num].status = MKEY_NORMAL;
				matrix[o+i*outputlines_num].timeout = 0;
			}
		}
		break;
	case MKEY_NORMAL:
	default:
		if(!val)
		{
			matrix[o+i*outputlines_num].status = MKEY_DET_PRESSED;
			matrix[o+i*outputlines_num].timeout = 0;
		}
		break;
	}
}

void MatrixKeyboard::_append_event(char e)
{
	for(int i = 0;i < queue_num; i++)
	{
		if(queue[i] == 0)
		{
			queue[i] = e;
			queue[i+1] = 0;
			break;
		}
	}
}

char MatrixKeyboard::getEvent()
{
	char e = queue[0];
	for(int i = 0;i < queue_num; i++)
	{
		if(queue[i])
			queue[i] = queue[i+1];
		else
			break;
	}
	return e;
}

void MatrixKeyboard::setLines(const s_MatrixKeyboardLine* output, const s_MatrixKeyboardLine* input)
{
	outputlines = output;
	inputlines = input;
}

void MatrixKeyboard::setAllowMultitriggering(bool trig)
{
	allow_multitriggering = trig;
}

void MatrixKeyboard::setValue(const char *val)
{
	martixvalue = val;
}


MatrixKeyboard::~MatrixKeyboard() {
	delete matrix;
}

} /* namespace bt_matrixkeyboard */
